/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <string>
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netdb.h> 

struct st
{
	int newsockfd;
	sockaddr_in client_in;	
};

void* rec(void* fd)
{
	int sockfd = *((int*)fd);
	char buffer[255];
	while(true){
		memset(buffer, 0, sizeof(buffer));
		int n = read(sockfd, buffer, 255);
		if(n < 0) {
			std::cerr << "ERROR reading from socket" << std::endl;
		}
		std::cout << buffer << std::endl;
	}
}

void* send(void* fd)
{
	int sockfd = *((int*)fd);
	std::cout << "Please enter the message:";
	char buffer[256];
	while(true){
		memset(buffer, 0, sizeof(buffer));
		std::cin >> buffer;
		int n = write(sockfd, buffer, strlen(buffer));
		if(n < 0) {
			std::cerr << "ERROR writing to socket" << std::endl;
		}
	}
}

void* chatting(void*)
{
	char h_name[255];
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		std::cerr << "Error opening socket" << std::endl;
	}
	std::cout << "enter host name to connect: ";
	std::cin >> h_name;
	//hostent* server = gethostbyname(h_name);
	//if (server == NULL) {
	//	std::cerr << "No such host" << std::endl;
	//}
	sockaddr_in serv_addr;
	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	inet_pton(AF_INET, h_name, &serv_addr.sin_addr);
        int portno = 6565;
	//std::cout << "enter host port number: ";
	//std::cin >> portno;
	serv_addr.sin_port = htons(portno);

	if(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
		std::cerr << "ERROR connecting" << std::endl;
	}
	pthread_t sd, rc;
	pthread_create( &sd, NULL, send, &sockfd);
	pthread_create( &rc, NULL, rec, &sockfd);
	pthread_join(sd, NULL);
	pthread_join(rc, NULL);

	close(sockfd);
}

void* connected(void* state)
{
	sockaddr_in cli_addr = ((st*)state)->client_in;
	int newsockfd = ((st*)state)->newsockfd;
	if (newsockfd < 0) {
		std::cerr << "ERROR on accept" << std::endl;
	}
	char* host_name = inet_ntoa(cli_addr.sin_addr);
	std::cout << "Connected to host: " << host_name << std::endl;
	pthread_t sd, rc;
	pthread_create( &rc, NULL, rec, &newsockfd);
	pthread_create( &sd, NULL, send, &newsockfd);
	pthread_join(sd, NULL);
	pthread_join(rc, NULL);

	close(newsockfd);
}

int main(int argc, char *argv[])
{
	pthread_t thread[100];
        //creates new socket IPv4 TCP and stores descriptor in sockfd
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
                std::cerr << "ERROR opening socket" << std::endl;
                return 1;
        }

        //Creates and initializes sock_addr_in structure.
        sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(sockaddr_in));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        // htons converts 16bit integer to network byte order.
        int portno = 6565;
	//std::cout << "enter server port number: ";
	//std::cin >> portno;
        serv_addr.sin_port = htons(portno);

        if(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
                std::cerr << "Can't bind socket: errno " << errno << std::endl;
		return 1;
	}
	// Start listening for incoming connections, 5 is size of backlog queue.
	listen(sockfd, 10);

	sockaddr_in cli_addr;
	socklen_t clilen = sizeof(cli_addr);

	int newsockfd ;
	int i = 0;
	//pthread_t cht;
	//pthread_create( &cht, NULL, chatting, NULL);
	while(newsockfd = accept(sockfd, (sockaddr*)&cli_addr, &clilen)){
		
		st cli;
		cli.client_in = cli_addr;
		cli.newsockfd = newsockfd;
		pthread_create( &thread[i], NULL, connected, &cli);
		i++;	
	}
	while(i >= 0){
		pthread_join(thread[i], NULL);
		i++;
	}
	//pthread_join(cht, NULL);

        close(sockfd);
        return 0; 
}
